package com.miyake.dsp.osa.ui;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;

import org.apache.commons.lang.ArrayUtils;

import com.miyake.dsp.osa.ChannelElement;
import com.miyake.dsp.osa.ChannelProperty;
import com.miyake.dsp.osa.lab.Fitting2;

public abstract class SignalEditorUI extends JFrame {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static final int WIDGET_HEIGHT = 24;
	private DefaultTableModel model;
	private ChannelProperty property;
	private JTextField startText;
	private JTextField spacingText;
	private JTextField countText;
	private JTextField deviationText;
	private JTextField levelText;
	
	abstract public void onSet(ChannelProperty property);

	public static void main(String[] arg) {
		ChannelProperty prop = new ChannelProperty();
		prop.setStartWavelength(1500);
		prop.setChannelCount(32);
		prop.setSpacingWavelength(0.4);
		new SignalEditorUI(prop) {

			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			public void onSet(ChannelProperty property) {
				// TODO Auto-generated method stub
				
			}
			
		}.setVisible(true);
	}
	
	public SignalEditorUI(ChannelProperty property) {
		this.property = property;
		initialize();
	}

	private void initialize() {
		this.setTitle("This function has not been implemented!!!!");
		
		this.setLayout(new FlowLayout());
		this.setSize(new Dimension(600, 600));
		Container c = this.getContentPane();
		c.setLayout(new FlowLayout());
		c.add(createSettingPanel());
		c.add(createEditPanel(property.getWavelengthDeviation(), property.getLevelDeviation()));
		c.add(createTablePanel());
		c.add(createOkButton());
		
		//updateTable();
	}

	private Component createEditPanel(double wavelength, double level) {
		JPanel p = new JPanel();
		p.setBorder(new TitledBorder("Edit"));
		
		JButton removeButton = new JButton("Deviate Level");
		p.add(removeButton);
		removeButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				createLevelData();
			}
		});
		levelText = new JTextField(String.valueOf(level));
		levelText.setPreferredSize(new Dimension(50, WIDGET_HEIGHT));
		p.add(levelText);
		
		JButton insertButton = new JButton("Deviate Wavelength");
		p.add(insertButton);
		insertButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				deviateWavelength();
			}
		});
		
		deviationText = new JTextField(String.valueOf(wavelength));
		deviationText.setPreferredSize(new Dimension(50, WIDGET_HEIGHT));
		p.add(deviationText);
		
		JButton gainButton = new JButton("Gain");
		p.add(gainButton);
		gainButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				fillGain();
			}
		});
		return p;
	}

	protected void fillGain() {
		int rows = model.getRowCount();
		double gain = 10;
		List<Double> wav = new LinkedList<Double>();
		List<Double> amp = new LinkedList<Double>();
		
		for (int row = 0; row < rows; row++) {
			String s = (String)model.getValueAt(row, 5);
			if (s != null && !s.isEmpty()) {
				wav.add(Double.valueOf((String)model.getValueAt(row, 0)));
				amp.add(Double.valueOf(s));
			}
		}

		String formula = "y=x(1)+x(2)*t+x(3)*t^2";
		//formula = "y=x(1)+x(3)*t*t";
		Fitting2 fitting = new Fitting2(formula);
		fitting.setOriginalData(ArrayUtils.toPrimitive(wav.toArray(new Double[0])), 
				ArrayUtils.toPrimitive(amp.toArray(new Double[0])));

		double[] x = new double[model.getRowCount()];
		for (int i = 0; i < x.length; i++) {
			x[i] = Double.valueOf((String)model.getValueAt(i, 0));
		}
		double[] ret = fitting.getInterpolated(x);
		for (int i = 0; i < rows; i++) {
			model.setValueAt(String.valueOf(ret[i]), i, 5);
		}
	}

	protected void deviateWavelength() {
		int rows = model.getRowCount();

		for (int i = 0; i < rows; i++) {
			double v = Double.valueOf((String)model.getValueAt(i, 1));
			double dev = Double.valueOf(this.deviationText.getText());
			model.setValueAt(String.valueOf(v + Math.random() * dev), i, 1);
			
			v = Double.valueOf((String)model.getValueAt(i, 3));
			model.setValueAt(String.valueOf(v + Math.random() * dev), i, 3);
		}	
	}

	private double getSpacing() {
		return Double.valueOf(this.spacingText.getText());
	}

	protected void createLevelData() {
		int rows = model.getRowCount();
		
		for (int i = 0; i < rows; i++) {
			double amp =Double.valueOf(this.levelText.getText()) * Math.random();
			model.setValueAt(String.valueOf(amp), i, 2);
			model.setValueAt(String.valueOf(amp), i, 4);
		}
	}

	protected void removeSelectedRow() {
		
	}

	private Component createSettingPanel() {
		JPanel p = new JPanel();
		p.setBorder(new TitledBorder("Setting"));
		
		p.add(new JLabel("Start Channel"));
		startText = new JTextField(String.format("%.3f", property.getStartWavelength()));
		startText.setPreferredSize(new Dimension(50, WIDGET_HEIGHT));
		p.add(startText);

		p.add(new JLabel("Spacing"));
		spacingText = new JTextField(String.format("%.3f", property.getSpacingWavelength()));
		spacingText.setPreferredSize(new Dimension(50, WIDGET_HEIGHT));
		p.add(spacingText);
		
		p.add(new JLabel("Channel Count"));
		countText = new JTextField(String.valueOf(property.getChannelCount()));
		countText.setPreferredSize(new Dimension(50, WIDGET_HEIGHT));
		p.add(countText);
		
		JButton button = new JButton("Create New");
		p.add(button);
		button.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				property.setChannelCount(Integer.valueOf(countText.getText()));
				property.setSpacingWavelength(Double.valueOf(spacingText.getText()));
				property.setStartWavelength(Double.valueOf(startText.getText()));
				updateTable();
			}
		});
		return p;
	}

	private void updateTable() {
		clearTable();
		for (int i = 0; i < property.getChannelCount(); i++) {
			String[] row = new String[5];
			row[0] = String.valueOf(i + 1);
			row[1] = String.format("%.3f", property.getStartWavelength() + property.getSpacingWavelength() * (double)i);
			row[2] = "0";
			row[3] = String.format("%.3f", property.getStartWavelength() + property.getSpacingWavelength() * (double)i);
			row[4] = "0";
			model.addRow(row);	
		}
	}

	private void clearTable() {
		int rows = model.getRowCount();
		for (int i = 0; i < rows; i++) {
			model.removeRow(0);
		}
	}

	private Component createOkButton() {
		JButton button = new JButton();
		button.setText("Set");
		button.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				onSet(createData());
			}
		});
		return button;
	}

	private Component createTablePanel() {
		final String[] titles = {"CH", "IN WL[nm]", "Lvl Amp[dB]", "OUT WL[nm]", "Lvl Amp[dB]", "Gain [dB]"};
		model = new DefaultTableModel(titles, 0) {
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			public boolean isCellEditable(int arg0, int arg1) {
				if (arg1 >0 && arg1 <  titles.length) {
					return true;
				}
				return false;
			}
		};
		JTable table = new JTable(model);
		model.addTableModelListener(new TableModelListener(){
			@Override
			public void tableChanged(TableModelEvent arg0) {

			}
			
		});
		
		if (property.getInChannels() != null & !property.getInChannels().isEmpty()) {
			model.setRowCount(property.getChannelCount());
			Iterator<ChannelElement> itIn = property.getInChannels().iterator();
			Iterator<ChannelElement> itOut = property.getOutChannels().iterator();
			Map<Double, Double> mapGain = new HashMap<Double, Double>();
			for (ChannelElement e : property.getGains()) {
				mapGain.put(e.getWavelength(), e.getAmplitude());
			}
			for (int i = 0; i < property.getChannelCount(); i++) {
				ChannelElement eIn = itIn.next();
				ChannelElement eOut = itOut.next();
				model.setValueAt(i+1, i, 0);
				model.setValueAt(String.valueOf(eIn.getWavelength()), i, 1);
				model.setValueAt(String.valueOf(eIn.getAmplitude()), i, 2);
				model.setValueAt(String.valueOf(eOut.getWavelength()), i, 3);
				model.setValueAt(String.valueOf(eOut.getAmplitude()), i, 4);
				Double tmpGain = null;
				if ((tmpGain = mapGain.get(eIn.getWavelength())) != null) {
					model.setValueAt(String.valueOf(tmpGain), i, 5);
				}
			}
		}
		
		JScrollPane scroll = new JScrollPane(table);
		scroll.setPreferredSize(new Dimension(500,300));
		return scroll;
	}
		
	private ChannelProperty createData()  {
		ChannelProperty obj = new ChannelProperty();
		obj.setChannelCount(Integer.valueOf(countText.getText()));
		obj.setSpacingWavelength(Double.valueOf(this.spacingText.getText()));
		obj.setStartWavelength(Double.valueOf(this.startText.getText()));
		obj.setWavelengthDeviation(Double.valueOf(this.deviationText.getText()));
		List<ChannelElement> inList = obj.getInChannels();
		List<ChannelElement> outList = obj.getOutChannels();
		List<ChannelElement> gainList = obj.getGains();
		inList.clear();
		outList.clear();
		gainList.clear();
		
		for (int row = 0; row < model.getRowCount(); row++) {
			inList.add(new ChannelElement(Double.valueOf((String)model.getValueAt(row, 1)), 
					Double.valueOf((String)model.getValueAt(row, 2))));
			outList.add(new ChannelElement(Double.valueOf((String)model.getValueAt(row, 3)), 
					Double.valueOf((String)model.getValueAt(row, 4))));	
			String gainValue = (String)model.getValueAt(row, 5);
			if (gainValue != null && !gainValue.isEmpty()) {
				gainList.add(new ChannelElement(
						Double.valueOf((String)model.getValueAt(row, 1)),
						Double.valueOf(gainValue)));
			}
		}
		
		return obj;
	}
}
